import 'dart:async';
import 'dart:io';

typedef OnError = void Function(Object error, StackTrace? stackTrace);
typedef OnReceiver = void Function(dynamic message);
typedef OnSendeWill = void Function(dynamic message);
typedef OnSendeDid = void Function(dynamic message);
typedef OnClose = void Function();
typedef OnOpen = void Function();

class NWWebSocket {
  WebSocket? _ws;
  StreamSubscription? _streamSubscription;
  bool cancelOnError = true;

  // 默认为15秒，如果设置为null，就禁用ping。 此设置在连接前有效，连接后设置无效
  Duration? pingInterval = const Duration(seconds: 1);

  OnError? onError;
  OnReceiver? onReceiver;
  OnSendeWill? onSendeWill; // 即将发送
  OnSendeDid? onSendeDid; // 不代表发送成功，只是已经发送
  OnClose? onClose;
  OnOpen? onOpen;

  int get status => _ws?.readyState ?? WebSocket.closed;

  connect(String url,
      {Iterable<String>? protocols,
      Map<String, dynamic>? headers,
      CompressionOptions compression = CompressionOptions.compressionDefault,
      HttpClient? customClient,
      bool cancelOnError = true}) async {
    cancelOnError = cancelOnError;
    try {
      _streamSubscription?.onData(null);
      _streamSubscription?.cancel();
      _streamSubscription = null;
      _ws?.close();
      _ws = null;
      _ws = await WebSocket.connect(
        url,
        protocols: protocols,
        headers: headers,
        compression: compression,
        customClient: customClient,
      );
      if (_ws?.readyState == WebSocket.open) {
        _ws?.pingInterval = pingInterval;
        _streamSubscription = _ws?.listen(
          _onReceiver,
          onDone: () {
            _onClose();
          },
          onError: onError,
          cancelOnError: cancelOnError,
        );
        _onOpen();
      } else {
        _onClose();
      }
    } catch (e) {
      _onClose();
    }
  }

  _onReceiver(dynamic message) {
    if (onReceiver != null) {
      onReceiver!(message);
    }
  }

  _onOpen() {
    if (onOpen != null) {
      onOpen!();
    }
  }

  _onError(Object error, StackTrace? stackTrace) {
    _ws = null;
    if (onError != null) {
      onError!(error, stackTrace);
    }
  }

  _onClose() {
    _ws = null;
    if (onClose != null) {
      onClose!();
    }
  }

  sender<T>(T data) {
    if (onSendeWill != null) {
      onSendeWill!(data);
    }
    if (status == WebSocket.open) {
      _ws?.add(data);
      if (onSendeDid != null) {
        onSendeDid!(data);
      }
    } else {
      onError!(NWWebSocketException(NWWSErrorCode.senderClose), null);
      _onClose();
    }
  }

  close([int? code, String? reason]) async {
    if (status == WebSocket.open) {
      await _ws?.close(code, reason);
    }
    _streamSubscription?.onData(null);
    _streamSubscription?.cancel();
    _streamSubscription = null;
    _ws = null;
    //   OnError? onError;
    // OnReceiver? onReceiver;
    // OnSendeWill? onSendeWill; // 即将发送
    // OnSendeDid? onSendeDid; // 不代表发送成功，只是已经发送
    // OnClose? onClose;
    // OnOpen? onOpen;
    onError = null;
    onReceiver = null;
    onSendeWill = null;
    onSendeDid = null;
    onClose = null;
    onOpen = null;
  }
}

class NWWebSocketException implements IOException {
  final NWWSErrorCode code;

  NWWebSocketException(this.code);

  @override
  String toString() => "WebSocketException: ${code.describe}";
}

class NWWSErrorCode {
  final int rawValue;
  final String describe;
  const NWWSErrorCode(this.rawValue, this.describe);

  @override
  bool operator ==(Object other) =>
      (other is NWWSErrorCode) && (other.rawValue == rawValue);

  @override
  int get hashCode => rawValue;

  static const undefined = NWWSErrorCode(999, "Undefined");
  static const retryLimited = NWWSErrorCode(1, "重试超过上限");
  static const senderClose = NWWSErrorCode(2, "发送时连接处于关闭状态");
  static const close = NWWSErrorCode(3, "连接处于关闭状态");
}
